package stone926.mods.more_enchantments;

import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.event.registry.RegistryEntryAddedCallback;
import net.fabricmc.fabric.api.gamerule.v1.GameRuleFactory;
import net.fabricmc.fabric.api.gamerule.v1.GameRuleRegistry;
import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings;
import net.fabricmc.fabric.api.object.builder.v1.block.entity.FabricBlockEntityTypeBuilder;
import net.fabricmc.fabric.api.object.builder.v1.client.model.FabricModelPredicateProviderRegistry;
import net.fabricmc.fabric.api.particle.v1.FabricParticleTypes;
import net.minecraft.block.*;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.block.entity.BlockEntityType;
import net.minecraft.enchantment.Enchantment;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.SpawnGroup;
import net.minecraft.entity.effect.StatusEffect;
import net.minecraft.entity.effect.StatusEffectInstance;
import net.minecraft.entity.effect.StatusEffects;
import net.minecraft.item.*;
import net.minecraft.particle.DefaultParticleType;
import net.minecraft.potion.Potion;
import net.minecraft.sound.BlockSoundGroup;
import net.minecraft.util.Identifier;
import net.minecraft.util.Rarity;
import net.minecraft.util.registry.Registry;
import net.minecraft.world.GameRules;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import stone926.mods.more_enchantments.blocks.*;
import stone926.mods.more_enchantments.blocks.eneity.EnchantmentOperatorBlockEntity;
import stone926.mods.more_enchantments.effect.InstantVampireEffect;
import stone926.mods.more_enchantments.effect.MemorialCeremonyEffect;
import stone926.mods.more_enchantments.effect.VampireEffect;
import stone926.mods.more_enchantments.enchantments.*;
import stone926.mods.more_enchantments.enchantments.cleanUp.*;
import stone926.mods.more_enchantments.enchantments.creative.*;
import stone926.mods.more_enchantments.enchantments.damage.AbstractStoneDamageEnchantment;
import stone926.mods.more_enchantments.enchantments.damage.ButcherEnchantment;
import stone926.mods.more_enchantments.enchantments.damage.FishermanEnchantment;
import stone926.mods.more_enchantments.enchantments.damage.VillageDefenderEnchantment;
import stone926.mods.more_enchantments.enchantments.legendary.*;
import stone926.mods.more_enchantments.enchantments.negative.*;
import stone926.mods.more_enchantments.enums.ArrowSpawnPosition;
import stone926.mods.more_enchantments.items.AppleOfRedemptionItem;
import stone926.mods.more_enchantments.items.CompressedSpiritualStuffsItem;
import stone926.mods.more_enchantments.items.CompressedStonePickaxeItem;
import stone926.mods.more_enchantments.items.ExperienceConverterItem;
import stone926.mods.more_enchantments.items.ammunition.MixedGunpowderItem;
import stone926.mods.more_enchantments.items.ammunition.ShulkerGunpowderItem;
import stone926.mods.more_enchantments.items.ammunition.WitherSkullGunpowderItem;
import stone926.mods.more_enchantments.items.bow.RandomFireballBow;
import stone926.mods.more_enchantments.items.bow.ShulkerBow;
import stone926.mods.more_enchantments.items.bow.WitherSkullBow;
import stone926.mods.more_enchantments.items.component.ComponentItem;
import stone926.mods.more_enchantments.items.component.ExtractComponentItem;
import stone926.mods.more_enchantments.items.component.MoveComponentItem;
import stone926.mods.more_enchantments.util.BlockUtil;
import stone926.mods.more_enchantments.util.EnchantmentUtil;
import stone926.mods.more_enchantments.util.ItemUtil;
import stone926.mods.more_enchantments.util.potion.PotionList;

import java.util.List;

@SuppressWarnings("unused")
public class MoreEnchantmentsMod implements ModInitializer {

  static {
    RegistryEntryAddedCallback.event(Registry.ENCHANTMENT).register((rawId, id, enchantment) -> {
      if (enchantment instanceof AbstractStoneDamageEnchantment de) EnchantmentUtil.STONE_DAMAGE_ENCHANTMENTS.add(de);
      else if (enchantment instanceof AbstractCreativeOnlyEnchantment ce) EnchantmentUtil.CREATIVE_ONLY_ENCHANTMENTS.add(ce);
      else if (enchantment instanceof AbstractNegativeEnchantment ne) EnchantmentUtil.NEGATIVE_ENCHANTMENTS.add(ne);
      else if (enchantment instanceof AbstractCleanUpEnchantment cle) EnchantmentUtil.CLEAN_UP_ENCHANTMENTS.add(cle);
      else if (enchantment instanceof AbstractLegendaryEnchantment le) EnchantmentUtil.LEGENDARY_ENCHANTMENTS.add(le);
      else if (enchantment instanceof EffectEnchantment ee) EnchantmentUtil.EFFECT_ENCHANTMENTS.add(ee);
    });
  }

  public static final String MOD_ID = "stone_more_enchantments";
  public static final String MOD_ID_ABBR = "sme";

  public static final Logger LOGGER = LogManager.getLogger(MOD_ID);

  public static final AbstractStoneDamageEnchantment BUTCHER = ench("butcher", new ButcherEnchantment());
  public static final AbstractStoneDamageEnchantment VILLAGER_DEFENDER = ench("village_defender", new VillageDefenderEnchantment());
  public static final AbstractStoneDamageEnchantment FISHERMAN = ench("fisherman", new FishermanEnchantment());

  public static final Enchantment SHULKER_BULLET_SHOOTER = ench("shulker_bullet_shooter", new ShulkerBulletShooterEnchantment());
  public static final Enchantment WITHER_SKULL_SHOOTER = ench("wither_skull_shooter", new WitherSkullShooterEnchantment());
  public static final Enchantment DESTROY_CURSE = ench("destroy_curse", new DestroyCurseEnchantment());
  public static final Enchantment MULTIPLY_CURSE = ench("multiply_curse", new MultiplyCurseEnchantment());
  public static final Enchantment RANDOM_FIREBALL_SHOOTER = ench("random_fireball_shooter", new RandomFireballShooterEnchantment());
  public static final Enchantment VAMPIRE = ench("vampire", new VampireEnchantment());
  public static final Enchantment EXPERIENCE_LOOTING = ench("experience_looting", new ExperienceLootingEnchantment());
  public static final DecapitationEnchantment DECAPITATION = ench("decapitation", new DecapitationEnchantment());
  public static final Enchantment BOUNCE = ench("bounce", new BounceEnchantment());
  public static final Enchantment BOMB = ench("bomb", new BombEnchantment());
  public static final Enchantment COUNTERATTACK = ench("counterattack", new CounterattackEnchantment());
  public static final Enchantment WIN_PEOPLE_BY_VIRTUE = ench("win_people_by_virtue", new WinPeopleByVirtueEnchantment());
  public static final Enchantment COOLDOWN_SHORTER = ench("cooldown_shorter", new CooldownShorterEnchantment());
  public static final Enchantment SOLIDIFY_WALKER = ench("solidify_walker", new SolidifyWalkerEnchantment());
  public static final Enchantment HOVER = ench("hover", new HoverEnchantment());
  public static final Enchantment REINFORCED_GUNPOWDER = ench("reinforced_gunpowder", new ReinforcedGunpowderEnchantment());
  public static final Enchantment SWORD_WITH_EFFECT = ench("sword_with_effect", new SwordWithEffectEnchantment());
  public static final Enchantment AREA_EFFECT_CLOUD_DURATION_BOOST = ench("area_effect_cloud_duration_boost", new AreaEffectCloudDurationBoostEnchantment());
  public static final Enchantment POUR = ench("pour", new PourEnchantment());
  public static final Enchantment FERTILIZER = ench("fertilizer", new FertilizerEnchantment());
  public static final Enchantment FIREBALL_THROWER = ench("fireball_thrower", new FireballThrowerEnchantment());
  public static final Enchantment BLAZE = ench("blaze", new BlazeEnchantment());
  public static final Enchantment ENDER_DRAGON = ench("ender_dragon", new EnderDragonEnchantment());
  public static final Enchantment ENHANCED_SMALL_FIREBALL = ench("enhanced_small_fireball", new EnhancedSmallFireballEnchantment());
  public static final Enchantment SACRIFICE = ench("sacrifice", new SacrificeEnchantment());
  public static final Enchantment SACRIFICE_RESISTANCE = ench("sacrifice_resistance", new SacrificeResistanceEnchantment());
  public static final Enchantment MANDALA_RESISTANCE = ench("mandala_resistance", new MandalaResistanceEnchantment());
  public static final Enchantment ONSLAUGHT = ench("onslaught", new OnslaughtEnchantment());
  public static final Enchantment LARGE_CAPACITY = ench("large_capacity", new LargeCapacityEnchantment());
  public static final Enchantment ANTIDOTE = ench("antidote", new AntidoteEnchantment());
  public static final Enchantment FAST_DRINK = ench("fast_drink", new FastDrinkEnchantment());
  public static final Enchantment SOUL_EXTRACTION = ench("soul_extraction", new SoulExtractionEnchantment());
  public static final Enchantment TP_SUPPRESSION = ench("tp_suppression", new TpSuppressionEnchantment());
  public static final Enchantment STARVE = ench("starve", new StarveEnchantment());
  public static final Enchantment TIE = ench("tie", new TieEnchantment());
  public static final Enchantment THUNDER = ench("thunder", new ThunderEnchantment());
  public static final Enchantment FLASH = ench("flash", new FlashEnchantment());
  public static final Enchantment FIRE_IMMUNE = ench("fire_immune", new FireImmuneEnchantment());

  public static final EffectEnchantment SLOWNESS_ENCHANTMENT = ench("slowness", new EffectEnchantment(5, StatusEffects.SLOWNESS, 5));
  public static final EffectEnchantment WITHER_ENCHANTMENT = ench("wither", new EffectEnchantment(5, StatusEffects.WITHER, 5));
  public static final EffectEnchantment HUNGER_ENCHANTMENT = ench("hunger", new EffectEnchantment(5, StatusEffects.HUNGER, 10));
  public static final EffectEnchantment WEAKNESS_ENCHANTMENT = ench("weakness", new EffectEnchantment(6, StatusEffects.WEAKNESS, 15));
  public static final EffectEnchantment BLINDNESS_ENCHANTMENT = ench("blindness", new EffectEnchantment(3, StatusEffects.BLINDNESS, 20));

  public static final TenThousandArrowsEnchantment TEN_THOUSAND_ARROWS_AT_SHOOTER = ench("ten_thousand_arrows_shooter", new TenThousandArrowsEnchantment(ArrowSpawnPosition.SHOOTER)).setPosition(ArrowSpawnPosition.SHOOTER);
  public static final TenThousandArrowsEnchantment TEN_THOUSAND_ARROWS_AT_TARGET = ench("ten_thousand_arrows_target", new TenThousandArrowsEnchantment(ArrowSpawnPosition.TARGET)).setPosition(ArrowSpawnPosition.TARGET);

  public static final AbstractCleanUpEnchantment MOWER = ench("mower", new MowerEnchantment());
  public static final AbstractCleanUpEnchantment TRIMMER = ench("trimmer", new TrimmerEnchantment());
  public static final AbstractCleanUpEnchantment CULTIVATION = ench("cultivation", new CultivationEnchantment());
  public static final AbstractCleanUpEnchantment FIRE_EXTINGUISHER = ench("fire_extinguisher", new FireExtinguisherEnchantment());

  public static final AbstractNegativeEnchantment SUFFOCATION = ench("suffocation", new SuffocationEnchantment());
  public static final AbstractNegativeEnchantment AQUA_REJECTION = ench("aqua_rejection", new AquaRejectionEnchantment());
  public static final AbstractNegativeEnchantment FLIMSY = ench("flimsy", new FlimsyEnchantment());
  public static final AbstractNegativeEnchantment TEARING = ench("tearing", new TearingEnchantment());
  public static final AbstractNegativeEnchantment TRACTION = ench("traction", new TractionEnchantment());
  public static final AbstractNegativeEnchantment MAKE_AMENDS = ench("make_amends", new MakeAmendsEnchantment());
  public static final AbstractNegativeEnchantment BETRAYAL = ench("betrayal", new BetrayalEnchantment());
  public static final AbstractNegativeEnchantment BURNING = ench("burning", new BurningEnchantment());
  public static final AbstractNegativeEnchantment BREAK_LEG = ench("break_leg", new BreakLegEnchantment());
  public static final AbstractNegativeEnchantment THE_TARGET_OF_ALL = ench("the_target_of_all", new TheTargetOfAllEnchantment());
  public static final AbstractNegativeEnchantment EXPLOSION_CREATOR = ench("explosion_creator", new ExplosionCreatorEnchantment());
  public static final AbstractNegativeEnchantment LIGHTNING_ROD = ench("lightning_rod", new LightningRodEnchantment());

  public static final AbstractCreativeOnlyEnchantment ULTIMATE_WEAPON = ench("ultimate_weapon", new UltimateWeaponEnchantment());
  public static final AbstractCreativeOnlyEnchantment FALLING_BLOCK_CREATOR = ench("falling_block_creator", new FallingBlockCreatorEnchantment());
  public static final AbstractCreativeOnlyEnchantment FLY = ench("fly", new FlyEnchantment());
  public static final AbstractCreativeOnlyEnchantment DEATH = ench("death", new DeathEnchantment());
  public static final AbstractCreativeOnlyEnchantment INEXHAUSTIBLE = ench("inexhaustible", new InexhaustibleEnchantment());

  public static final AbstractLegendaryEnchantment PURGE = ench("purge", new PurgeEnchantment());
  public static final AbstractLegendaryEnchantment EXTINCTION = ench("extinction", new ExtinctionEnchantment());
  public static final AbstractLegendaryEnchantment STABLE = ench("stable", new StableEnchantment());
  public static final AbstractLegendaryEnchantment DAMAGE_BLOCKING = ench("damage_blocking", new DamageBlockingEnchantment());
  public static final AbstractLegendaryEnchantment ARMOR_BREAKING = ench("armor_breaking", new ArmorBreakingEnchantment());
  public static final AbstractLegendaryEnchantment FATE = ench("fate", new FateEnchantment());
  public static final AbstractLegendaryEnchantment UNBREAKABLE = ench("unbreakable", new UnbreakableEnchantment());
  public static final AbstractLegendaryEnchantment THUMP = ench("thump", new ThumpEnchantment());
  public static final AbstractLegendaryEnchantment RETALIATION = ench("retaliation", new RetaliationEnchantment());
  public static final AbstractLegendaryEnchantment STRAFING = ench("strafing", new StrafingEnchantment());
  public static final AbstractLegendaryEnchantment INFINITE = ench("infinite", new InfiniteEnchantment());

  public static final IceBlock SOLIDIFIED_LAVA = block("solidified_lava", new SolidifiedLavaBlock(AbstractBlock.Settings.of(Material.STONE, MapColor.DARK_RED).ticksRandomly().strength(0.5F).sounds(BlockSoundGroup.GLASS).nonOpaque().dropsNothing().allowsSpawning((state, world, pos, entityType) -> entityType.isFireImmune())));

  public static final Block EFFECT_ADDER = block("effect_adder", new EffectAdderBlock(AbstractBlock.Settings.of(Material.WOOD).strength(2.5F).sounds(BlockSoundGroup.WOOD).allowsSpawning((state, blockView, pos, entityType) -> true)));
  public static final BlockItem EFFECT_ADDER_ITEM = item("effect_adder", new BlockItem(EFFECT_ADDER, new Item.Settings().group(ItemGroup.DECORATIONS)));

  public static final FlowerBlock BLACK_MANDALA = block("black_mandala", new BlackMandalaFlowerBlock(StatusEffects.INSTANT_DAMAGE, 2, AbstractBlock.Settings.of(Material.PLANT).noCollision().breakInstantly().sounds(BlockSoundGroup.GRASS).ticksRandomly().allowsSpawning(((state, blockView, pos, entityType) -> entityType.getSpawnGroup() == SpawnGroup.MONSTER))));
  public static final FlowerBlock CHRYSANTHEMUM = block("chrysanthemum", new ChrysanthemumFlowerBlock(StatusEffects.HASTE, 8, AbstractBlock.Settings.of(Material.PLANT).noCollision().breakInstantly().sounds(BlockSoundGroup.GRASS).ticksRandomly().allowsSpawning((state, blockView, pos, entityType) -> entityType.getSpawnGroup() != SpawnGroup.MONSTER)));
  public static final FlowerBlock CORNEL = block("cornel", new CornelFlowerBlock(StatusEffects.HEALTH_BOOST, 10, AbstractBlock.Settings.copy(CHRYSANTHEMUM).requiresTool()));

  public static final BlockItem CORNEL_ITEM = item("cornel", new BlockItem(CORNEL, new Item.Settings().group(ItemGroup.DECORATIONS)));
  public static final BlockItem CHRYSANTHEMUM_ITEM = item("chrysanthemum", new BlockItem(CHRYSANTHEMUM, new Item.Settings().group(ItemGroup.DECORATIONS)));
  public static final BlockItem BLACK_MANDALA_ITEM = item("black_mandala", new BlockItem(BLACK_MANDALA, new Item.Settings().group(ItemGroup.DECORATIONS)));
  public static final Block GOLDEN_ANVIL_BLOCK = block("golden_anvil", new GoldenAnvilBlock(AbstractBlock.Settings.of(Material.METAL, MapColor.GOLD).requiresTool().strength(5.0F, 1200.0F).sounds(BlockSoundGroup.ANVIL)));
  public static final BlockItem GOLDEN_ANVIL = item("golden_anvil", new BlockItem(GOLDEN_ANVIL_BLOCK, new Item.Settings().group(ItemGroup.DECORATIONS)));
  public static final Block CHIPPED_GOLDEN_ANVIL_BLOCK = block("chipped_golden_anvil", new GoldenAnvilBlock(AbstractBlock.Settings.of(Material.METAL, MapColor.GOLD).requiresTool().strength(5.0F, 1200.0F).sounds(BlockSoundGroup.ANVIL)));
  public static final BlockItem CHIPPED_GOLDEN_ANVIL = item("chipped_golden_anvil", new BlockItem(CHIPPED_GOLDEN_ANVIL_BLOCK, new Item.Settings().group(ItemGroup.DECORATIONS)));

  public static final Block DAMAGED_GOLDEN_ANVIL_BLOCK = block("damaged_golden_anvil", new GoldenAnvilBlock(AbstractBlock.Settings.of(Material.METAL, MapColor.GOLD).requiresTool().strength(5.0F, 1200.0F).sounds(BlockSoundGroup.ANVIL)));
  public static final BlockItem DAMAGED_GOLDEN_ANVIL = item("damaged_golden_anvil", new BlockItem(DAMAGED_GOLDEN_ANVIL_BLOCK, new Item.Settings().group(ItemGroup.DECORATIONS)));
  public static final Block COMPRESSED_STONE_BLOCK = block("compressed_stone", new CompressedStone(FabricBlockSettings.of(Material.STONE, MapColor.STONE_GRAY).requiresTool().strength(10F, 1000F).sounds(BlockSoundGroup.STONE)));
  public static final BlockItem COMPRESSED_STONE_ITEM = item("compressed_stone", new BlockItem(COMPRESSED_STONE_BLOCK, new Item.Settings().group(ItemGroup.BUILDING_BLOCKS)));
  public static final Block ENCHANTMENT_OPERATOR_BLOCK = block("enchantment_operator", new EnchantmentOperatorBlock(FabricBlockSettings.of(Material.METAL, MapColor.GRAY).strength(3F, 20F).sounds(BlockSoundGroup.METAL)));
  public static final BlockItem ENCHANTMENT_OPERATOR_ITEM = item("enchantment_operator", new BlockItem(ENCHANTMENT_OPERATOR_BLOCK, new Item.Settings().group(ItemGroup.REDSTONE)));

  public static final Item IRON_GOLEM_SPAWN_EGG = item("iron_golem_spawn_egg", new SpawnEggItem(EntityType.IRON_GOLEM, 0xd6d6d6, 0x74a332, new Item.Settings().group(ItemGroup.MISC)));
  public static final Item WITHER_SPAWN_EGG = item("wither_spawn_egg", new SpawnEggItem(EntityType.WITHER, 0x171717, 0x486d98, new Item.Settings().group(ItemGroup.MISC)));
  public static final Item EXPERIENCE_CONVERTER = item("experience_converter", new ExperienceConverterItem(new Item.Settings().group(ItemGroup.MISC).maxDamage(300).fireproof().rarity(Rarity.UNCOMMON)));
  public static final Item APPLE_OF_REDEMPTION_RANGE = item("apple_of_redemption_range", new AppleOfRedemptionItem(new Item.Settings().group(ItemGroup.MISC).maxDamage(20).rarity(Rarity.RARE), AppleOfRedemptionItem.Type.RANGE));
  public static final Item APPLE_OF_REDEMPTION_SINGLE = item("apple_of_redemption_single", new AppleOfRedemptionItem(new Item.Settings().group(ItemGroup.MISC).maxDamage(60).rarity(Rarity.RARE), AppleOfRedemptionItem.Type.SINGLE));
  public static final Item COMPRESSED_STONE_PICKAXE = item("compressed_stone_pickaxe", new CompressedStonePickaxeItem(new Item.Settings().group(ItemGroup.TOOLS).maxDamage(200)));
  public static final Item COMPRESSED_SPIRITUAL_STUFFS = item("compressed_spiritual_stuffs", new CompressedSpiritualStuffsItem(new Item.Settings().group(ItemGroup.MISC)));
  public static final Item WITHER_SKULL_GUNPOWDER = item("wither_skull_gunpowder", new WitherSkullGunpowderItem(new Item.Settings().group(ItemGroup.COMBAT).maxCount(64)));
  public static final Item SHULKER_GUNPOWDER = item("shulker_gunpowder", new ShulkerGunpowderItem(new Item.Settings().group(ItemGroup.COMBAT).maxCount(64)));
  public static final Item MIXED_GUNPOWDER = item("mixed_gunpowder", new MixedGunpowderItem(new Item.Settings().group(ItemGroup.COMBAT).maxCount(64)));

  public static final ComponentItem BASIC_COMPONENT = item("basic_component", new ComponentItem(new Item.Settings().group(ItemGroup.REDSTONE)));

  public static final ExtractComponentItem EXTRACT_COMPONENT_HEAD = item("extract_component_head", new ExtractComponentItem(new Item.Settings().group(ItemGroup.REDSTONE)));
  public static final ExtractComponentItem EXTRACT_COMPONENT_REAR = item("extract_component_rear", new ExtractComponentItem(new Item.Settings().group(ItemGroup.REDSTONE)));
  public static final ExtractComponentItem EXTRACT_COMPONENT_DIRECT = item("extract_component_direct", new ExtractComponentItem(new Item.Settings().group(ItemGroup.REDSTONE)));

  public static final MoveComponentItem MOVE_COMPONENT_FORWARD = item("move_component_forward", new MoveComponentItem(new Item.Settings().group(ItemGroup.REDSTONE)));
  public static final MoveComponentItem MOVE_COMPONENT_BACKWARD = item("move_component_backward", new MoveComponentItem(new Item.Settings().group(ItemGroup.REDSTONE)));

  public static final ComponentItem UPGRADE_COMPONENT = item("upgrade_component", new ComponentItem(new Item.Settings().group(ItemGroup.REDSTONE)));
  public static final ComponentItem RESET_COMPONENT = item("reset_component", new ComponentItem(new Item.Settings().group(ItemGroup.REDSTONE)));
  public static final ComponentItem CLEAR_COMPONENT = item("clear_component", new ComponentItem(new Item.Settings().group(ItemGroup.REDSTONE)));
  public static final ComponentItem SPLIT_COMPONENT = item("split_component", new ComponentItem(new Item.Settings().group(ItemGroup.REDSTONE)));
  public static final ComponentItem FORCE_ENCHANT_COMPONENT = item("force_enchant_component", new ComponentItem(new Item.Settings().group(ItemGroup.REDSTONE)));

  public static final BowItem WITHER_SKULL_BOW = item("wither_skull_bow", new WitherSkullBow(new Item.Settings().maxDamage(390).group(ItemGroup.COMBAT)));
  public static final BowItem FIREBALL_BOW = item("fireball_bow", new RandomFireballBow(new Item.Settings().maxDamage(380).group(ItemGroup.COMBAT)));
  public static final BowItem SHULKER_BOW = item("shulker_bow", new ShulkerBow(new Item.Settings().maxDamage(350).group(ItemGroup.COMBAT)));

  public static final DefaultParticleType DAMAGE_BLOCKED = particle("damage_blocked", FabricParticleTypes.simple());
  public static final DefaultParticleType TRIDENT_RAIN = particle("trident_rain", FabricParticleTypes.simple());
  public static final DefaultParticleType VAMPIRE_HEART = particle("vampire_heart", FabricParticleTypes.simple());
  public static final DefaultParticleType WATER_BREATHING_REMOVED = particle("water_breathing_removed", FabricParticleTypes.simple());

  public static final StatusEffect VAMPIRE_EFFECT = effect("vampire", new VampireEffect());
  public static final StatusEffect MEMORIAL_CEREMONY_EFFECT = effect("memorial_ceremony", new MemorialCeremonyEffect());
  public static final StatusEffect INSTANT_VAMPIRE_EFFECT = effect("instant_vampire", new InstantVampireEffect());

  public static final Potion INSTANT_VAMPIRE_POTION = potion(INSTANT_VAMPIRE_EFFECT, "instant_vampire", 1, 1);
  public static final Potion STRONG_INSTANT_VAMPIRE_POTION = potion(INSTANT_VAMPIRE_EFFECT, "strong_instant_vampire", 1, 2);
  public static final Potion VAMPIRE_POTION = potion(VAMPIRE_EFFECT, "vampire", 200, 1);
  public static final Potion STRONG_VAMPIRE_POTION = potion(VAMPIRE_EFFECT, "strong_vampire", 200, 2);
  public static final Potion LONG_VAMPIRE_POTION = potion(VAMPIRE_EFFECT, "long_vampire", 300, 1);
  public static final Potion WITHER = potion(StatusEffects.WITHER, "wither", 800, 0);
  public static final Potion LONG_WITHER = potion(StatusEffects.WITHER, "long_wither", 1600, 0);
  public static final Potion STRONG_WITHER = potion(StatusEffects.WITHER, "strong_wither", 700, 1);
  public static final Potion NAUSEA = potion(StatusEffects.NAUSEA, "nausea", 400, 0);
  public static final Potion LONG_NAUSEA = potion(StatusEffects.NAUSEA, "long_nausea", 1000, 0);
  public static final Potion HEALTH_BOOST = potion(StatusEffects.HEALTH_BOOST, "health_boost", 800, 0);
  public static final Potion LONG_HEALTH_BOOST = potion(StatusEffects.HEALTH_BOOST, "long_health_boost", 1800, 0);
  public static final Potion STRONG_HEALTH_BOOST = potion(StatusEffects.HEALTH_BOOST, "strong_health_boost", 800, 2);
  public static final Potion LEVITATION = potion(StatusEffects.LEVITATION, "levitation", 200, 2);
  public static final Potion LONG_LEVITATION = potion(StatusEffects.LEVITATION, "long_levitation", 400, 2);
  public static final Potion STRONG_LEVITATION = potion(StatusEffects.LEVITATION, "strong_levitation", 180, 3);
  public static final Potion RESISTANCE = potion(StatusEffects.RESISTANCE, "resistance", 400, 2);
  public static final Potion LONG_RESISTANCE = potion(StatusEffects.RESISTANCE, "long_resistance", 700, 2);
  public static final Potion STRONG_RESISTANCE = potion(StatusEffects.RESISTANCE, "strong_resistance", 400, 3);
  public static final Potion DOLPHINS_GRACE = potion(StatusEffects.DOLPHINS_GRACE, "dolphins_grace", 4000, 0);
  public static final Potion LONG_DOLPHINS_GRACE = potion(StatusEffects.DOLPHINS_GRACE, "long_dolphins_grace", 8000, 0);
  public static final Potion HASTE = potion(StatusEffects.HASTE, "haste", 1200, 0);
  public static final Potion LONG_HASTE = potion(StatusEffects.HASTE, "long_haste", 2500, 0);
  public static final Potion STRONG_HASTE = potion(StatusEffects.HASTE, "strong_haste", 1200, 1);
  public static final Potion VERY_STRONG_HASTE = potion(StatusEffects.HASTE, "very_strong_haste", 1200, 2);
  public static final Potion MINING_FATIGUE = potion(StatusEffects.MINING_FATIGUE, "mining_fatigue", 1200, 0);
  public static final Potion LONG_MINING_FATIGUE = potion(StatusEffects.MINING_FATIGUE, "long_mining_fatigue", 2500, 0);
  public static final Potion STRONG_MINING_FATIGUE = potion(StatusEffects.MINING_FATIGUE, "strong_mining_fatigue", 1200, 1);
  public static final Potion VERY_STRONG_MINING_FATIGUE = potion(StatusEffects.MINING_FATIGUE, "very_strong_mining_fatigue", 1200, 2);
  public static final Potion LUCK = potion(StatusEffects.LUCK, "luck", 3000, 1);
  public static final Potion LONG_LUCK = potion(StatusEffects.LUCK, "long_luck", 6500, 1);
  public static final Potion STRONG_LUCK = potion(StatusEffects.LUCK, "strong_luck", 3000, 2);
  public static final Potion UNLUCK = potion(StatusEffects.UNLUCK, "unluck", 3000, 1);
  public static final Potion LONG_UNLUCK = potion(StatusEffects.UNLUCK, "long_unluck", 6500, 1);
  public static final Potion STRONG_UNLUCK = potion(StatusEffects.UNLUCK, "strong_unluck", 3000, 2);
  public static final Potion ABSORPTION = potion(StatusEffects.ABSORPTION, "absorption", 600, 1);
  public static final Potion LONG_ABSORPTION = potion(StatusEffects.ABSORPTION, "long_absorption", 1500, 1);
  public static final Potion STRONG_ABSORPTION = potion(StatusEffects.ABSORPTION, "strong_absorption", 600, 2);
  public static final Potion BAD_OMEN = potion(StatusEffects.BAD_OMEN, "bad_omen", 999999, 1);
  public static final Potion STRONG_BAD_OMEN = potion(StatusEffects.BAD_OMEN, "strong_bad_omen", 999999, 2);
  public static final Potion VERY_STRONG_BAD_OMEN = potion(StatusEffects.BAD_OMEN, "very_strong_bad_omen", 999999, 3);
  public static final Potion HERO_OF_THE_VILLAGE = potion(StatusEffects.HERO_OF_THE_VILLAGE, "hero_of_the_village", 10000, 1);
  public static final Potion STRONG_HERO_OF_THE_VILLAGE = potion(StatusEffects.HERO_OF_THE_VILLAGE, "strong_hero_of_the_village", 140000, 2);
  public static final Potion VERY_STRONG_HERO_OF_THE_VILLAGE = potion(StatusEffects.HERO_OF_THE_VILLAGE, "very_strong_hero_of_the_village", 18000, 3);
  public static final Potion GLOWING = potion(StatusEffects.GLOWING, "glowing", 2500, 0);
  public static final Potion LONG_GLOWING = potion(StatusEffects.GLOWING, "long_glowing", 5000, 0);
  public static final Potion BLINDNESS = potion(StatusEffects.GLOWING, "blindness", 100, 0);
  public static final Potion LONG_BLINDNESS = potion(StatusEffects.GLOWING, "long_blindness", 200, 0);
  public static final Potion SATURATION = potion(StatusEffects.SATURATION, "saturation", 120, 1);
  public static final Potion LONG_SATURATION = potion(StatusEffects.SATURATION, "long_saturation", 240, 1);
  public static final Potion STRONG_SATURATION = potion(StatusEffects.SATURATION, "strong_saturation", 240, 2);

  public static final GameRules.Key<GameRules.BooleanRule> BLACK_MANDALA_SPAWNING = GameRuleRegistry.register("blackMandalaSpawning", GameRules.Category.SPAWNING, GameRuleFactory.createBooleanRule(true));
  public static final GameRules.Key<GameRules.BooleanRule> FLOWER_ACTION_REPORT = GameRuleRegistry.register("flowerActionReport", GameRules.Category.MISC, GameRuleFactory.createBooleanRule(false));
  public static final GameRules.Key<GameRules.BooleanRule> CORNEL_ACTIVE = GameRuleRegistry.register("cornelActive", GameRules.Category.MISC, GameRuleFactory.createBooleanRule(true));

  public static final BlockEntityType<EnchantmentOperatorBlockEntity> ENCHANTMENT_OPERATOR_BLOCK_ENTITY_TYPE = blockEntityType("enchantment_operator_block_entity_type", FabricBlockEntityTypeBuilder.create(EnchantmentOperatorBlockEntity::new, ENCHANTMENT_OPERATOR_BLOCK).build());

  static {
    FabricModelPredicateProviderRegistry.register(WITHER_SKULL_BOW, new Identifier("pull"), (itemStack, clientWorld, livingEntity, seed) -> {
      if (livingEntity == null) {
        return 0.0F;
      }
      return livingEntity.getActiveItem() != itemStack ? 0.0F : (itemStack.getMaxUseTime() - livingEntity.getItemUseTimeLeft()) / 20.0F;
    });

    FabricModelPredicateProviderRegistry.register(WITHER_SKULL_BOW, new Identifier("pulling"), (itemStack, clientWorld, livingEntity, seed) -> {
      if (livingEntity == null) {
        return 0.0F;
      }
      return livingEntity.isUsingItem() && livingEntity.getActiveItem() == itemStack ? 1.0F : 0.0F;
    });


    FabricModelPredicateProviderRegistry.register(FIREBALL_BOW, new Identifier("pull"), (itemStack, clientWorld, livingEntity, seed) -> {
      if (livingEntity == null) {
        return 0.0F;
      }
      return livingEntity.getActiveItem() != itemStack ? 0.0F : (itemStack.getMaxUseTime() - livingEntity.getItemUseTimeLeft()) / 20.0F;
    });

    FabricModelPredicateProviderRegistry.register(FIREBALL_BOW, new Identifier("pulling"), (itemStack, clientWorld, livingEntity, seed) -> {
      if (livingEntity == null) {
        return 0.0F;
      }
      return livingEntity.isUsingItem() && livingEntity.getActiveItem() == itemStack ? 1.0F : 0.0F;
    });


    FabricModelPredicateProviderRegistry.register(SHULKER_BOW, new Identifier("pull"), (itemStack, clientWorld, livingEntity, seed) -> {
      if (livingEntity == null) {
        return 0.0F;
      }
      return livingEntity.getActiveItem() != itemStack ? 0.0F : (itemStack.getMaxUseTime() - livingEntity.getItemUseTimeLeft()) / 20.0F;
    });

    FabricModelPredicateProviderRegistry.register(SHULKER_BOW, new Identifier("pulling"), (itemStack, clientWorld, livingEntity, seed) -> {
      if (livingEntity == null) {
        return 0.0F;
      }
      return livingEntity.isUsingItem() && livingEntity.getActiveItem() == itemStack ? 1.0F : 0.0F;
    });
  }

  public static Identifier id(String s) {
    return new Identifier(MOD_ID, s);
  }

  private static <T extends Enchantment> T ench(String id, T enchantment) {
    return Registry.register(Registry.ENCHANTMENT, id(id), enchantment);
  }

  private static <T extends Block> T block(String id, T block) {
    return Registry.register(Registry.BLOCK, id(id), block);
  }

  private static <T extends Item> T item(String id, T item) {
    return Registry.register(Registry.ITEM, id(id), item);
  }

  private static <T extends StatusEffect> T effect(String id, T effect) {
    return Registry.register(Registry.STATUS_EFFECT, id(id), effect);
  }

  private static <T extends DefaultParticleType> T particle(String id, T particle) {
    return Registry.register(Registry.PARTICLE_TYPE, id(id), particle);
  }

  private static <T extends BlockEntity> BlockEntityType<T> blockEntityType(String id, BlockEntityType<T> blockEntityType) {
    return Registry.register(Registry.BLOCK_ENTITY_TYPE, id, blockEntityType);
  }

  private static Potion potion(StatusEffect effect, String id, int duration, int amplifier) {
    Potion potion = new Potion(new StatusEffectInstance(effect, duration, amplifier));
    Potion register = Registry.register(Registry.POTION, id(id), potion);
    PotionList.POTION.put(effect, potion);
    PotionList.ADDED_POTIONS.add(register);
    return register;
  }

  public static String addAbbrIdPrefix(String s) {
    return MOD_ID_ABBR + ":" + s;
  }

  public static String addIdPrefix(String s) {
    return MOD_ID + ":" + s;
  }

  @Override
  public void onInitialize() {
    ItemUtil.HELMETS.addAll(List.of((ArmorItem) Items.LEATHER_HELMET, (ArmorItem) Items.CHAINMAIL_HELMET, (ArmorItem) Items.IRON_HELMET, (ArmorItem) Items.DIAMOND_HELMET, (ArmorItem) Items.GOLDEN_HELMET, (ArmorItem) Items.NETHERITE_HELMET));
    ItemUtil.CHESTPLATES.addAll(List.of((ArmorItem) Items.LEATHER_CHESTPLATE, (ArmorItem) Items.CHAINMAIL_CHESTPLATE, (ArmorItem) Items.IRON_CHESTPLATE, (ArmorItem) Items.DIAMOND_CHESTPLATE, (ArmorItem) Items.GOLDEN_CHESTPLATE, (ArmorItem) Items.NETHERITE_CHESTPLATE));
    ItemUtil.LEGGINGS.addAll(List.of((ArmorItem) Items.LEATHER_LEGGINGS, (ArmorItem) Items.CHAINMAIL_LEGGINGS, (ArmorItem) Items.IRON_LEGGINGS, (ArmorItem) Items.DIAMOND_LEGGINGS, (ArmorItem) Items.GOLDEN_LEGGINGS, (ArmorItem) Items.NETHERITE_LEGGINGS));
    ItemUtil.BOOTS.addAll(List.of((ArmorItem) Items.LEATHER_BOOTS, (ArmorItem) Items.CHAINMAIL_BOOTS, (ArmorItem) Items.IRON_BOOTS, (ArmorItem) Items.DIAMOND_BOOTS, (ArmorItem) Items.GOLDEN_BOOTS, (ArmorItem) Items.NETHERITE_BOOTS));

    ItemUtil.SWORDS.addAll(List.of((SwordItem) Items.WOODEN_SWORD, (SwordItem) Items.GOLDEN_SWORD, (SwordItem) Items.IRON_SWORD, (SwordItem) Items.STONE_SWORD, (SwordItem) Items.DIAMOND_SWORD, (SwordItem) Items.NETHERITE_SWORD));
    ItemUtil.AXES.addAll(List.of((AxeItem) Items.WOODEN_AXE, (AxeItem) Items.GOLDEN_AXE, (AxeItem) Items.IRON_AXE, (AxeItem) Items.STONE_AXE, (AxeItem) Items.DIAMOND_AXE, (AxeItem) Items.NETHERITE_AXE));

    ItemUtil.SHOVELS.addAll(List.of((ShovelItem) Items.WOODEN_SHOVEL, (ShovelItem) Items.GOLDEN_SHOVEL, (ShovelItem) Items.IRON_SHOVEL, (ShovelItem) Items.STONE_SHOVEL, (ShovelItem) Items.DIAMOND_SHOVEL, (ShovelItem) Items.NETHERITE_SHOVEL));
    ItemUtil.HOES.addAll(List.of((HoeItem) Items.WOODEN_HOE, (HoeItem) Items.GOLDEN_HOE, (HoeItem) Items.IRON_HOE, (HoeItem) Items.STONE_HOE, (HoeItem) Items.DIAMOND_HOE, (HoeItem) Items.NETHERITE_HOE));
    ItemUtil.PICKAXES.addAll(List.of((PickaxeItem) Items.WOODEN_PICKAXE, (PickaxeItem) Items.GOLDEN_PICKAXE, (PickaxeItem) Items.IRON_PICKAXE, (PickaxeItem) Items.STONE_PICKAXE, (PickaxeItem) Items.DIAMOND_PICKAXE, (PickaxeItem) Items.NETHERITE_PICKAXE));

    BlockUtil.PANE_BLOCKS.addAll(List.of((PaneBlock) Blocks.GLASS_PANE, (PaneBlock) Blocks.WHITE_STAINED_GLASS_PANE, (PaneBlock) Blocks.ORANGE_STAINED_GLASS_PANE, (PaneBlock) Blocks.MAGENTA_STAINED_GLASS_PANE, (PaneBlock) Blocks.LIGHT_BLUE_STAINED_GLASS_PANE, (PaneBlock) Blocks.YELLOW_STAINED_GLASS_PANE, (PaneBlock) Blocks.LIME_STAINED_GLASS_PANE, (PaneBlock) Blocks.PINK_STAINED_GLASS_PANE, (PaneBlock) Blocks.GRAY_STAINED_GLASS_PANE, (PaneBlock) Blocks.LIGHT_GRAY_STAINED_GLASS_PANE, (PaneBlock) Blocks.CYAN_STAINED_GLASS_PANE, (PaneBlock) Blocks.PURPLE_STAINED_GLASS_PANE, (PaneBlock) Blocks.BLUE_STAINED_GLASS_PANE, (PaneBlock) Blocks.BROWN_STAINED_GLASS_PANE, (PaneBlock) Blocks.GREEN_STAINED_GLASS_PANE, (PaneBlock) Blocks.RED_STAINED_GLASS_PANE, (PaneBlock) Blocks.BLACK_STAINED_GLASS_PANE));
    BlockUtil.STAINED_GLASS_BLOCKS.addAll(List.of((StainedGlassBlock) Blocks.WHITE_STAINED_GLASS, (StainedGlassBlock) Blocks.ORANGE_STAINED_GLASS, (StainedGlassBlock) Blocks.MAGENTA_STAINED_GLASS, (StainedGlassBlock) Blocks.LIGHT_BLUE_STAINED_GLASS, (StainedGlassBlock) Blocks.YELLOW_STAINED_GLASS, (StainedGlassBlock) Blocks.LIME_STAINED_GLASS, (StainedGlassBlock) Blocks.PINK_STAINED_GLASS, (StainedGlassBlock) Blocks.GRAY_STAINED_GLASS, (StainedGlassBlock) Blocks.LIGHT_GRAY_STAINED_GLASS, (StainedGlassBlock) Blocks.CYAN_STAINED_GLASS, (StainedGlassBlock) Blocks.PURPLE_STAINED_GLASS, (StainedGlassBlock) Blocks.BLUE_STAINED_GLASS, (StainedGlassBlock) Blocks.BROWN_STAINED_GLASS, (StainedGlassBlock) Blocks.GREEN_STAINED_GLASS, (StainedGlassBlock) Blocks.RED_STAINED_GLASS, (StainedGlassBlock) Blocks.BLACK_STAINED_GLASS));

    BlockUtil.GLASS_BLOCKS.add((AbstractGlassBlock) Blocks.TINTED_GLASS);
    BlockUtil.GLASS_BLOCKS.add((AbstractGlassBlock) Blocks.GLASS);

    BlockUtil.WOOL_BLOCKS.addAll(List.of(Blocks.WHITE_WOOL, Blocks.ORANGE_WOOL, Blocks.MAGENTA_WOOL, Blocks.LIGHT_BLUE_WOOL, Blocks.YELLOW_WOOL, Blocks.LIME_WOOL, Blocks.PINK_WOOL, Blocks.GRAY_WOOL, Blocks.LIGHT_GRAY_WOOL, Blocks.CYAN_WOOL, Blocks.PURPLE_WOOL, Blocks.BLUE_WOOL, Blocks.BROWN_WOOL, Blocks.GREEN_WOOL, Blocks.RED_WOOL, Blocks.BLACK_WOOL));

    BlockUtil.CORAL_BLOCKS.addAll(List.of(Blocks.BRAIN_CORAL_BLOCK, Blocks.BUBBLE_CORAL_BLOCK, Blocks.FIRE_CORAL_BLOCK, Blocks.HORN_CORAL_BLOCK, Blocks.TUBE_CORAL_BLOCK));
    BlockUtil.DEAD_CORAL_BLOCKS.addAll(List.of(Blocks.DEAD_BRAIN_CORAL_BLOCK, Blocks.DEAD_BUBBLE_CORAL_BLOCK, Blocks.DEAD_FIRE_CORAL_BLOCK, Blocks.DEAD_HORN_CORAL_BLOCK, Blocks.DEAD_TUBE_CORAL_BLOCK));

    BlockUtil.CORALS.addAll(List.of(Blocks.BRAIN_CORAL, Blocks.BUBBLE_CORAL, Blocks.FIRE_CORAL, Blocks.HORN_CORAL, Blocks.TUBE_CORAL));
    BlockUtil.DEAD_CORALS.addAll(List.of(Blocks.DEAD_BRAIN_CORAL, Blocks.DEAD_BUBBLE_CORAL, Blocks.DEAD_FIRE_CORAL, Blocks.DEAD_HORN_CORAL, Blocks.DEAD_TUBE_CORAL));

    BlockUtil.CORAL_FANS.addAll(List.of(Blocks.BRAIN_CORAL_FAN, Blocks.BUBBLE_CORAL_FAN, Blocks.FIRE_CORAL_FAN, Blocks.HORN_CORAL_FAN, Blocks.TUBE_CORAL_FAN));
    BlockUtil.DEAD_CORAL_FANS.addAll(List.of(Blocks.DEAD_BRAIN_CORAL_FAN, Blocks.DEAD_BUBBLE_CORAL_FAN, Blocks.DEAD_FIRE_CORAL_FAN, Blocks.DEAD_HORN_CORAL_FAN, Blocks.DEAD_TUBE_CORAL_FAN));
  }

}
